/**********************************************************************************************************************
 * \file Echo.c
 * \brief TCP回显服务器实现，用于以太网通信演示
 *********************************************************************************************************************/

/*********************************************************************************************************************/
/*-----------------------------------------------------Includes------------------------------------------------------*/
/*********************************************************************************************************************/
#include "Echo.h"
#include <string.h>
#include "lwip/tcp.h"

/*********************************************************************************************************************/
/*------------------------------------------------------Macros-------------------------------------------------------*/
/*********************************************************************************************************************/
#define STORAGE_SIZE_BYTES 256          /* 用于存储接收数据的内存大小（字节） */

/*********************************************************************************************************************/
/*-------------------------------------------------Data Structures---------------------------------------------------*/
/*********************************************************************************************************************/
enum EchoStates                         /* 与远程用户会话的状态 */
{
    ES_NONE = 0,                        /* 会话未完全初始化 */
    ES_ACCEPTED,                        /* 会话已分配给远程客户端并正在分配资源 */
    ES_RECEIVING,                       /* 会话正在接收来自远程客户端发送的数据 */
    ES_CLOSING                          /* 会话正在关闭，分配的资源将被释放 */
};

typedef struct tcp_pcb tcpPcb;          /* 定义更方便的类型 */
typedef struct pbuf pBuf;               /* 定义更方便的类型 */

typedef struct                          /* 用于与单个远程客户端通信的会话数据结构 */
{
    u8_t state;                         /* 会话的当前状态 */
    tcpPcb *pcb;                        /* 用于此会话的TCP协议控制块的指针 */
    pBuf *p;                            /* 用于存储接收数据包的缓冲区指针 */
    char storage[STORAGE_SIZE_BYTES];   /* 接收字符串的存储区域 */
    uint16 nextFreeStoragePos;          /* 存储数组中下一个空闲位置的位置 */
} EchoSession;

/*********************************************************************************************************************/
/*-------------------------------------------------Global variables--------------------------------------------------*/
/*********************************************************************************************************************/
/* 欢迎信息，在每次新连接时显示 */
char * g_Logo = "Welcome to TCP Echo Server\r\n";

tcpPcb *g_echoPcb;                     /* TCP协议控制块指针 */

/*********************************************************************************************************************/
/*------------------------------------------------Function Prototypes------------------------------------------------*/
/*********************************************************************************************************************/
err_t echoAccept (void *arg, tcpPcb *newPcb, err_t err);            /* 当客户端建立新连接时调用的接受回调函数 */
err_t echoRecv (void *arg, tcpPcb *tpcb, pBuf *p, err_t err);       /* 每次通过TCP连接接收数据时调用的接收回调函数 */
void echoError (void *arg, err_t err);                              /* 当连接上发生致命错误时调用的错误回调函数 */
err_t echoPoll (void *arg, tcpPcb *tpcb);                           /* 由TCP栈定期调用的轮询函数 */
err_t echoSent (void *arg, tcpPcb *tpcb, u16_t len);                /* 当TCP数据成功传输到远程主机时调用的发送回调函数 */
void echoSend (tcpPcb *tPcb, EchoSession *es);                      /* 将TCP数据加入队列以便传输到远程客户端的发送函数 */
void echoUnpack (tcpPcb *tPcb, EchoSession *es);                    /* 从包缓冲区中提取数据并将其复制到会话存储中的解包函数 */
void echoClose (tcpPcb *tPcb, EchoSession *es);                     /* 关闭TCP连接并释放会话资源的关闭函数 */

/*********************************************************************************************************************/
/*---------------------------------------------Function Implementations----------------------------------------------*/
/*********************************************************************************************************************/
/* 
 * 函数名称：echoInit
 * 功能描述：初始化TCP回显服务器，创建并配置TCP控制块
 * 端口：80 (HTTP默认端口)
 */
void echoInit(void)
{
    /* 创建TCP协议控制块 */
    g_echoPcb = tcp_new();
    if (g_echoPcb != NULL)
    {
        /* 绑定到任意本地地址和80端口 */
        err_t err = tcp_bind(g_echoPcb, IP_ADDR_ANY, 80);
        if (err == ERR_OK)
        {
            /* 设置为监听模式 */
            g_echoPcb = tcp_listen(g_echoPcb);
            /* 注册连接接受回调函数 */
            tcp_accept(g_echoPcb, echoAccept);
        }
    }
}

/* 
 * 函数名称：echoAccept
 * 功能描述：连接接受回调函数，当客户端建立新连接时被调用
 * 参数：
 *   arg - 回调参数
 *   newPcb - 新连接的TCP控制块
 *   err - 错误码
 * 返回值：操作结果
 */
err_t echoAccept(void *arg, tcpPcb *newPcb, err_t err)
{
    /* 分配会话内存 */
    EchoSession *es = (EchoSession*) mem_malloc(sizeof(EchoSession));
    if (es != NULL)
    {
        /* 初始化会话 */
        es->state = ES_ACCEPTED;
        es->pcb = newPcb;
        es->p = NULL;
        memset(es->storage, 0, STORAGE_SIZE_BYTES);
        es->nextFreeStoragePos = 0;

        /* 配置回调函数 */
        tcp_arg(newPcb, es);
        tcp_recv(newPcb, echoRecv);
        tcp_sent(newPcb, echoSent);
        tcp_err(newPcb, echoError);
        tcp_poll(newPcb, echoPoll, 0);

        /* 发送欢迎信息 */
        tcp_write(newPcb, g_Logo, strlen(g_Logo), 1);
        return ERR_OK;
    }
    return ERR_MEM; /* 内存分配失败 */
}

/* 
 * 函数名称：echoRecv
 * 功能描述：数据接收回调函数，当通过TCP连接接收数据时被调用
 * 参数：
 *   arg - 回调参数
 *   tpcb - TCP控制块
 *   p - 接收的数据包
 *   err - 错误码
 * 返回值：操作结果
 */
err_t echoRecv(void *arg, tcpPcb *tpcb, pBuf *p, err_t err)
{
    EchoSession *es = (EchoSession*) arg;

    /* 客户端关闭连接 */
    if (p == NULL)
    {
        es->state = ES_CLOSING;
        if (es->p == NULL)
        {
            echoClose(tpcb, es);
        }
        else
        {
            echoUnpack(tpcb, es);
            echoSend(tpcb, es);
        }
        return ERR_OK;
    }
    /* 接收错误处理 */
    else if (err != ERR_OK)
    {
        if (p != NULL)
        {
            es->p = NULL;
            pbuf_free(p);
        }
        return err;
    }
    /* 首次接收数据 */
    else if (es->state == ES_ACCEPTED)
    {
        es->state = ES_RECEIVING;
        es->p = p;
        echoUnpack(tpcb, es);
        echoSend(tpcb, es);
        return ERR_OK;
    }
    /* 继续接收数据 */
    else if (es->state == ES_RECEIVING)
    {
        if (es->p == NULL)
        {
            es->p = p;
            echoUnpack(tpcb, es);
            echoSend(tpcb, es);
        }
        else
        {
            /* 链接数据包 */
            pbuf_chain(es->p, p);
        }
        return ERR_OK;
    }
    /* 未知状态，丢弃数据 */
    else
    {
        tcp_recved(tpcb, p->tot_len);
        es->p = NULL;
        pbuf_free(p);
        return ERR_OK;
    }
}

/* 
 * 函数名称：echoError
 * 功能描述：错误回调函数，当连接发生致命错误时被调用
 * 参数：
 *   arg - 回调参数
 *   err - 错误码
 */
void echoError(void *arg, err_t err)
{
    EchoSession *es = (EchoSession*) arg;
    if (es != NULL)
    {
        mem_free(es); /* 释放会话内存 */
    }
}

/* 
 * 函数名称：echoPoll
 * 功能描述：轮询函数，由TCP栈定期调用
 * 参数：
 *   arg - 回调参数
 *   tpcb - TCP控制块
 * 返回值：操作结果
 */
err_t echoPoll(void *arg, tcpPcb *tpcb)
{
    EchoSession *es = (EchoSession*) arg;

    if (es != NULL)
    {
        /* 处理未处理的数据并发送 */
        if (es->p != NULL || es->nextFreeStoragePos != 0)
        {
            echoUnpack(tpcb, es);
            echoSend(tpcb, es);
        }
        else if (es->state == ES_CLOSING)
        {
            echoClose(tpcb, es); /* 关闭连接 */
        }
        return ERR_OK;
    }
    else
    {
        tcp_abort(tpcb); /* 中止连接 */
        return ERR_ABRT;
    }
}

/* 
 * 函数名称：echoSent
 * 功能描述：数据发送完成回调函数，当TCP数据成功发送到远程主机时被调用
 * 参数：
 *   arg - 回调参数
 *   tpcb - TCP控制块
 *   len - 发送的数据长度
 * 返回值：操作结果
 */
err_t echoSent(void *arg, tcpPcb *tpcb, u16_t len)
{
    EchoSession *es = (EchoSession*) arg;

    /* 处理剩余数据 */
    if (es->p != NULL)
    {
        echoUnpack(tpcb, es);
        echoSend(tpcb, es);
    }
    else if (es->state == ES_CLOSING)
    {
        echoClose(tpcb, es); /* 关闭连接 */
    }
    return ERR_OK;
}

/* 
 * 函数名称：echoSend
 * 功能描述：数据发送函数，将数据加入发送队列
 * 参数：
 *   tpcb - TCP控制块
 *   es - 会话数据
 */
void echoSend(tcpPcb *tpcb, EchoSession *es)
{
    /* 无数据可发送 */
    if(es->nextFreeStoragePos == 0)
    {
        return;
    }
    
    /* 等待换行符或缓冲区填满 */
    if(es->storage[es->nextFreeStoragePos - 1] != '\n' &&
       es->nextFreeStoragePos < STORAGE_SIZE_BYTES)
    {
        return;
    }
    
    /* 发送回显数据 */
    err_t wrErr = tcp_write(tpcb, "Echo: ", 6, 1);
    wrErr |= tcp_write(tpcb, es->storage, es->nextFreeStoragePos, 1);
    
    if(wrErr == ERR_OK)
    {
        es->nextFreeStoragePos = 0; /* 重置存储位置 */
    }
}

/* 
 * 函数名称：echoUnpack
 * 功能描述：数据解包函数，从数据包缓冲区提取数据并复制到会话存储区
 * 参数：
 *   tpcb - TCP控制块
 *   es - 会话数据
 */
void echoUnpack(tcpPcb *tpcb, EchoSession *es)
{
    pBuf *ptr;

    /* 处理所有数据包 */
    while ((es->p != NULL))
    {
        ptr = es->p;

        /* 检查存储空间是否充足 */
        if (es->nextFreeStoragePos + ptr->len <= STORAGE_SIZE_BYTES)
        {
            /* 复制数据到存储区 */
            memcpy(&es->storage[es->nextFreeStoragePos], ptr->payload, ptr->len);
            es->nextFreeStoragePos += ptr->len;

            u16_t plen = ptr->len;

            /* 获取下一个数据包 */
            es->p = ptr->next;
            if (es->p != NULL)
            {
                pbuf_ref(es->p); /* 增加引用计数 */
            }

            /* 释放当前数据包 */
            u8_t freed;
            do
            {
                freed = pbuf_free(ptr);
            }
            while (freed == 0);

            /* 通知对端可以继续发送数据 */
            tcp_recved(tpcb, plen);
        }
    }
}

/* 
 * 函数名称：echoClose
 * 功能描述：关闭函数，关闭TCP连接并释放会话资源
 * 参数：
 *   tpcb - TCP控制块
 *   es - 会话数据
 */
void echoClose(tcpPcb *tpcb, EchoSession *es)
{
    /* 清除回调函数 */
    tcp_arg(tpcb, NULL);
    tcp_sent(tpcb, NULL);
    tcp_recv(tpcb, NULL);
    tcp_err(tpcb, NULL);
    tcp_poll(tpcb, NULL, 0);

    /* 释放会话内存 */
    if (es != NULL)
    {
        mem_free(es);
    }
    
    /* 关闭TCP连接 */
    tcp_close(tpcb);
}
